/* QtLua -- Lua bindings for Qt
   Copyright (C) 2011, Jarek Pelczar

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA

*/

#include "qluascriptcontext_p.h"
#include "qluascriptengine_p.h"
#include "qluascriptable_p.h"
#include "qluascriptengineagent.h"
#include "qluascriptcontextinfo_p.h"

QLuaScriptContextPrivate::~QLuaScriptContextPrivate()
{
    if(m_scriptableObject) {
        QLuaScriptable * scriptableInterface = qobject_cast<QLuaScriptable *>(m_scriptableObject.data());
        Q_ASSERT(scriptableInterface != NULL);
        QLuaScriptablePrivate * p = QLuaScriptablePrivate::priv(scriptableInterface);
        p->m_context = m_prevScriptableContext;
        p->m_engine = m_prevScriptableEngine;
    }
    delete m_contextInfo;
}

void QLuaScriptContextPrivate::setupInfo(lua_Debug * ar)
{
    Q_ASSERT(m_contextInfo == NULL);
    m_contextInfo = new QLuaScriptContextInfoPrivate();

    m_contextInfo->m_engine = QLuaScriptEnginePrivate::priv(m_engine);
    m_contextInfo->m_contextPrivate = this;

    QByteArray fileName(ar->source);

    m_contextInfo->m_scriptName = QString::fromUtf8(fileName.constData(), fileName.length());
    m_contextInfo->m_scriptId = m_contextInfo->m_engine->m_fileNameToProgramId.value(fileName, -1);
    m_contextInfo->m_startLine = ar->linedefined;
    m_contextInfo->m_endLine = ar->lastlinedefined;
    m_contextInfo->m_currentLine = ar->currentline;
    m_contextInfo->m_functionName = QString::fromUtf8(ar->name);

    if(ar->what) {
        if(ar->what[0] == 'L') {
            m_contextInfo->m_type = QLuaScriptContextInfo::ScriptFunction;
        } else if(ar->what[0] == 'C') {
            m_contextInfo->m_type = QLuaScriptContextInfo::NativeFunction;
        } else if(ar->what[0] == 'm') {
            m_contextInfo->m_type = QLuaScriptContextInfo::MainChunk;
        } else if(ar->what[0] == 't') {
            m_contextInfo->m_type = QLuaScriptContextInfo::TailCall;
        } else {
            m_contextInfo->m_type = QLuaScriptContextInfo::UnknownFunction;
        }
    } else {
        m_contextInfo->m_type = QLuaScriptContextInfo::UnknownFunction;
    }

    m_contextInfo->m_upValueCount = ar->nups;

#ifdef QT_DEBUG
    qDebug() << "[QtLua] Enter" << m_contextInfo->m_functionName << "@" << m_contextInfo->m_scriptName;
#endif
}

void QLuaScriptContextPrivate::pushRecord()
{
    int recordId = 0;
    QLuaScriptContextPrivate * ptr = this;
    while(ptr) {
        ptr->m_activationRecordId = recordId++;
        if(!ptr->m_parent)
            break;
        ptr = QLuaScriptContextPrivate::priv(ptr->m_parent);
    }
}

void QLuaScriptContextPrivate::popRecord()
{
    if(m_parent) {
        QLuaScriptContextPrivate::priv(m_parent)->pushRecord();
    }
}

QLuaScriptContext::QLuaScriptContext(QLuaScriptContextPrivate * dd)
{
    d = dd;
}

QLuaScriptContext::~QLuaScriptContext()
{
    delete d;
}

QLuaScriptValue QLuaScriptContext::argument(int index) const
{
    return d->m_args.value(index);
}

int QLuaScriptContext::argumentCount() const
{
    return d->m_args.count();
}

QLuaScriptEngine * QLuaScriptContext::engine() const
{
    return d->m_engine;
}

QLuaScriptContext * QLuaScriptContext::parentContext() const
{
    return d->m_parent;
}

void QLuaScriptContext::setThisObject(const QLuaScriptValue& obj)
{
    d->m_thisObject = obj;
}

QLuaScriptValue QLuaScriptContext::thisObject() const
{
    return d->m_thisObject;
}

namespace QLuaInternal {

QLuaScriptContextCleanup::QLuaScriptContextCleanup(QLuaScriptEnginePrivate * enginePrivate,
                         QLuaScriptContext * activeContext,
                         QVector<QLuaScriptValue> args,
                         const QLuaScriptValue& self,
                         QObject * scriptable)
{
    QLuaScriptContextPrivate * priv = QLuaScriptContextPrivate::priv(activeContext);
    priv->m_args = args;
    priv->m_thisObject = self;

    if(QLuaScriptable * scriptableInterface = qobject_cast<QLuaScriptable *>(scriptable))
    {
        QLuaScriptablePrivate * p = QLuaScriptablePrivate::priv(scriptableInterface);
        priv->m_prevScriptableContext = p->m_context;
        priv->m_prevScriptableEngine = p->m_engine;
        p->m_context = activeContext;
        p->m_engine = enginePrivate->q_ptr;        
        priv->m_scriptableObject = scriptable;
    }

    this->m_contextInfo = priv->m_contextInfo;
}

void QLuaScriptContextCleanup::overrideFunctionType(QLuaScriptContextInfo::FunctionType type)
{
    if(m_contextInfo) {
        m_contextInfo->m_type = type;
    }
}

}
